package com.huishu.goods.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huishu.commons.exception.BaseException;
import com.huishu.commons.exception.NotFoundException;
import com.huishu.commons.pojo.Result;
import com.huishu.commons.pojo.dto.DeductionStockDTO;
import com.huishu.commons.pojo.dto.FindAvailableCouponDTO;
import com.huishu.commons.pojo.dto.UrlManageDTO;
import com.huishu.commons.pojo.vo.CartItemGoodsVO;
import com.huishu.commons.pojo.vo.CouponVO;
import com.huishu.commons.pojo.vo.FullReductionVO;
import com.huishu.commons.pojo.vo.GiveProductVO;
import com.huishu.commons.utlis.BeanUtils;
import com.huishu.goods.fegin.CouponFeignClient;
import com.huishu.goods.fegin.OrderFeignClient;
import com.huishu.goods.fegin.UserFeignClient;
import com.huishu.goods.mapper.ProductGiveMapper;
import com.huishu.goods.mapper.ProductMapper;
import com.huishu.goods.pojo.dto.CategoryProductDTO;
import com.huishu.goods.pojo.dto.ProductCreateDTO;
import com.huishu.goods.pojo.dto.ProductPageDTO;
import com.huishu.goods.pojo.dto.ProductUpdateDTO;
import com.huishu.goods.pojo.model.*;
import com.huishu.goods.pojo.vo.*;
import com.huishu.goods.service.*;
import com.huishu.goods.utils.AssistantContext;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author generator@ljj
 * @since 2023-04-08
 */
@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> implements ProductService {

    private static final String URL_NAME = "/pages/goods/goodsDetails/goodsDetails?id=";

    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private Executor executor;

    @Autowired
    private ProductCategoryService productCategoryService;

    @Autowired
    private ProductLabelService productLabelService;

    @Autowired
    private ProductFullReductionService productFullReductionService;

    @Autowired
    private CouponFeignClient couponFeignClient;

    @Autowired
    private ProductGiveMapper productGiveMapper;

    @Autowired
    private ProductGiveService productGiveService;

    @Autowired
    private ProductItemService productItemService;

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private MaterialBenefitsProductGiveService materialBenefitsProductGiveService;

    @Autowired
    private NewproductBestsellingService newproductBestsellingService;

    @Override
    public Result categoryProduct(CategoryProductDTO categoryProductDTO) {
        CategoryProductVO categoryProductVO = new CategoryProductVO();
        try {
            CompletableFuture<IPage<CategoryProductVO.Product>> future = CompletableFuture.supplyAsync(() -> {
                // 查询商品数据
                Page<CategoryProductVO.Product> page = new Page<>(categoryProductDTO.getCurrPage(), categoryProductDTO.getPageSize());
                return this.baseMapper.categoryProduct(page, categoryProductDTO);
            }, executor);
            CompletableFuture<List<ProductCategoryDO>> future1 = null;
            CompletableFuture<List<ProductLabelDO>> future2 = null;
            if (categoryProductDTO.getIsFindCategory()) {

                future1 = CompletableFuture.supplyAsync(() -> {
                    // 查询品类
                    LambdaQueryWrapper<ProductCategoryDO> queryWrapper = Wrappers.lambdaQuery(ProductCategoryDO.class)
                            .eq(ProductCategoryDO::getIsShow, true)
                            .orderByAsc(ProductCategoryDO::getSort);
                    return productCategoryService.list(queryWrapper);
                }, executor);
                future2 = CompletableFuture.supplyAsync(() -> {
                    LambdaQueryWrapper<ProductLabelDO> queryWrapperProductLabelDO = Wrappers.lambdaQuery(ProductLabelDO.class)
                            .eq(ProductLabelDO::getIsShow, true)
                            .orderByAsc(ProductLabelDO::getSort);
                    return productLabelService.list(queryWrapperProductLabelDO);
                }, executor);
                CompletableFuture<Void> allOf = CompletableFuture.allOf(future, future1, future2);
                allOf.get();
                categoryProductVO.setProductIPage(future.get());
                categoryProductVO.setProductCategorys(future1 == null ? new ArrayList<>() : future1.get());
                categoryProductVO.setProductLabels(future2 == null ? new ArrayList<>() : future2.get());
            }
            CompletableFuture<Void> allOf = CompletableFuture.allOf(future);
            allOf.get();
            categoryProductVO.setProductIPage(future.get());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("查询categoryProduct异常:{}", e.getMessage());
            throw new BaseException("查询categoryProduct异常");
        }
        return Result.success(categoryProductVO);
    }

    @Override
    public Result findNewProductOrBestsellingOrSample(CategoryProductDTO categoryProductDTO) {
        Page<CategoryProductVO.Product> page = new Page<>(categoryProductDTO.getCurrPage(), categoryProductDTO.getPageSize());
        IPage<CategoryProductVO.Product> productIPage = this.baseMapper.categoryProduct(page, categoryProductDTO);
        return Result.success(productIPage);
    }

    @Override
    public Result detailed(Long id) {
        try {
            // 查询商品详情
            ProductDO productDO = getById(id);
            if (productDO == null) {
                return Result.fail("当前商品已经下架啦");
            }
            if (productDO.getIsPublish() == 0) {
                return Result.fail("当前商品已经下架啦");
            }
            Result<List<FullReductionVO>> result2 = productFullReductionService.find(Arrays.asList(id));
            ProductDetailedVO productDetailedVO = BeanUtils.copyProperties(productDO, ProductDetailedVO.class);
            LambdaQueryWrapper<ProductItemDO> queryWrapper = Wrappers.lambdaQuery(ProductItemDO.class)
                    .eq(ProductItemDO::getProductId, productDO.getId());
            productDetailedVO.setProductItems(productItemService.list(queryWrapper));
            if (productDO.getIsSample()) {
                return Result.success(productDetailedVO);
            }
            List<FullReductionVO> entry = result2.getEntry();
            if (CollectionUtils.isNotEmpty(entry)) {
                productDetailedVO.setFullReductions(entry);
                entry = entry.stream().sorted(Comparator.comparing(FullReductionVO::getFullPrice).reversed()).collect(Collectors.toList());
                // 获取最大满减信息名称
                FullReductionVO fullReductionVO = entry.get(0);
                productDetailedVO.setMaxFullReductionName(fullReductionVO.getName());
            }

            // 获取当前商品最大优惠政策
            CompletableFuture<List<CouponVO>> future1 = CompletableFuture.supplyAsync(() -> {
                FindAvailableCouponDTO findAvailableCouponDTO = new FindAvailableCouponDTO();
                findAvailableCouponDTO.setProductCategoryIds(Collections.singletonList(productDO.getProductCategoryId()));
                Result<List<CouponVO>> result = couponFeignClient.findAvailableCoupon(findAvailableCouponDTO);
                if (!result.getStatus()) {
                    throw new NotFoundException("根据商品查询优惠卷异常");
                }
                return result.getEntry();
            }, executor);
            List<CouponVO> coupon = future1.get();
            if (CollectionUtils.isNotEmpty(coupon)) {
                coupon = coupon.stream().sorted(Comparator.comparing(CouponVO::getAmount).reversed()).collect(Collectors.toList());
                productDetailedVO.setMaxCouponName(coupon.get(0).getName());
            }
            if (StringUtils.isNotEmpty(productDO.getRecommenIds())) {
                List<String> result = Arrays.asList(productDO.getRecommenIds().split(","));
                List<Long> ids = result.stream().map(Long::parseLong).collect(Collectors.toList());
                productDetailedVO.setRecommenProducts(this.baseMapper.findRecommenProducts(ids));
            }
            return Result.success(productDetailedVO);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new BaseException(e.getMessage());
        }
    }

    @Override
    public Result getCouponByGoods(Long id) {
        // 查询商品
        ProductDO productDO = getById(id);
        try {
            // 查询优惠卷
            CompletableFuture<List<CouponVO>> future1 = CompletableFuture.supplyAsync(() -> {
                FindAvailableCouponDTO findAvailableCouponDTO = new FindAvailableCouponDTO();
                findAvailableCouponDTO.setProductCategoryIds(Collections.singletonList(productDO.getProductCategoryId()));
                Result<List<CouponVO>> result = couponFeignClient.findAvailableCoupon(findAvailableCouponDTO);
                if (!result.getStatus()) {
                    throw new NotFoundException("根据商品查询优惠卷异常");
                }
                return result.getEntry();
            }, executor);

            // 查询我的优惠卷
            CompletableFuture<List<CouponVO>> future2 = CompletableFuture.supplyAsync(() -> {
                // 由于子线程fegin调用 会拿不到父线程的request所以分开查询优惠卷
                if (AssistantContext.getCurrentUid() != null) {
                    Result<List<CouponVO>> result = couponFeignClient.findCouponByUserId(AssistantContext.getCurrentUid(),
                            Collections.singletonList(productDO.getProductCategoryId()));
                    if (!result.getStatus()) {
                        log.error("根据商品id查询优惠卷异常");
                        throw new NotFoundException("根据商品id查询优惠卷异常");
                    }
                    return result.getEntry();
                }
                return null;
            }, executor);
            // 查询我的复购卷
            CompletableFuture<List<CouponVO>> future3 = CompletableFuture.supplyAsync(() -> {
                if (AssistantContext.getCurrentUid() != null) {
                    Result<List<CouponVO>> result = couponFeignClient.findRepurchaseByUserId(AssistantContext.getCurrentUid(), Collections.singletonList(productDO.getProductCategoryId()));
                    if (!result.getStatus()) {
                        log.error("查询查询我的复购卷异常");
                        throw new NotFoundException("查询查询我的复购卷异常");
                    }
                    return result.getEntry();
                }
                return null;
            }, executor);
            CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
            allOf.get();
            List<CouponVO> coupons = future1.get();
            List<CouponVO> couponAll = new ArrayList<>();
            if (AssistantContext.getCurrentUid() != null) {
                // 用户登入状态下
                // 当前商品品类1   品类1下面优惠卷  优惠卷A  优惠卷C
                // 用户优惠卷中    优惠卷A 优惠卷B
                // 需要把优惠卷B打上不可使用标识
                List<CouponVO> userCoupons = future2.get();
                for (CouponVO userCoupon : userCoupons) {
                    if (CollectionUtils.isNotEmpty(coupons)) {
                        if (coupons.contains(userCoupon)) {
                            coupons.remove(userCoupon);
                        } else {
                            userCoupon.setIsUse(false);
                        }
                    }
                }
                if (CollectionUtils.isNotEmpty(coupons)) {
                    couponAll.addAll(coupons.stream().peek(c -> c.setIsReceive(false)).collect(Collectors.toList()));
                }
                couponAll.addAll(userCoupons);
            }
            List<CouponVO> couponVOS = future3.get();
            if (CollectionUtils.isNotEmpty(couponVOS)) {
                couponAll.addAll(couponVOS);
            }
            return Result.success(couponAll);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.fail("查询失败");
    }

    @Override
    public Result findCartRecommen() {
        return Result.success(this.baseMapper.findCartRecommen());
    }

    @Override
    public Result<List<CartItemGoodsVO>> findCartItemGoods(List<Long> ids) {
        List<CartItemGoodsVO> cartItemGoods = this.baseMapper.findCartItemGoods(ids);
        // 查询赠品
        List<Long> goodsIds = cartItemGoods.stream().map(CartItemGoodsVO::getProductId).collect(Collectors.toList());
        List<GiveProductVO> giveProducts = productGiveMapper.findGiveByProductIds(goodsIds, 1);
        Map<Long, List<GiveProductVO>> giveProductMap = giveProducts.stream().collect(Collectors.groupingBy(GiveProductVO::getProductId));
        for (CartItemGoodsVO cartItemGood : cartItemGoods) {
            cartItemGood.setGiveProducts(giveProductMap.get(cartItemGood.getProductId()));
        }
        return Result.success(cartItemGoods);
    }


    @Override
    public Result deductionStock(List<DeductionStockDTO> deductionStockDTOS) {
        if (CollectionUtils.isEmpty(deductionStockDTOS)) {
            return Result.success();
        }
        ReentrantLock reentrantLock = new ReentrantLock(true);
        try {
            reentrantLock.lock();
            this.baseMapper.deductionStock(deductionStockDTOS);
        } finally {
            reentrantLock.unlock();
        }
        return Result.success();
    }

    @Override
    public Result search(CategoryProductDTO categoryProductDTO) {
        Page<CategoryProductVO.Product> page = new Page<>(categoryProductDTO.getCurrPage(), categoryProductDTO.getPageSize());
        return Result.success(this.baseMapper.categoryProduct(page, categoryProductDTO));
    }


    @Transactional
    @Override
    public Result create(ProductCreateDTO productCreateDTO) {
        ProductDO productDO = BeanUtils.copyProperties(productCreateDTO, ProductDO.class);
        productDO.setIsVerify(1);
        boolean save = this.save(productDO);
        if (!save) {
            throw new BaseException("添加商品失败");
        }
        List<ProductItemDO> collect = productCreateDTO.getProductItems()
                .stream().map(productItemDO -> productItemDO.setProductId(productDO.getId())).collect(Collectors.toList());
        boolean b = productItemService.saveBatch(collect);
        if (!b) {
            throw new BaseException("添加商品规格参数失败");
        }
        UrlManageDTO urlManageDTO = new UrlManageDTO();
        urlManageDTO.setName(productCreateDTO.getName());
        urlManageDTO.setType(3);
        urlManageDTO.setUrl(URL_NAME + productDO.getId());
        Result result = userFeignClient.createUrl(urlManageDTO);
        if (!result.getStatus()) {
            throw new BaseException("添加商品url失败！！");
        }
        return Result.success();
    }

    @GlobalTransactional
    @Override
    public Result modify(ProductUpdateDTO productUpdateDTO) {
        ProductDO productDO = BeanUtils.copyProperties(productUpdateDTO, ProductDO.class);
        boolean save = this.updateById(productDO);
        if (!save) {
            throw new BaseException("修改商品失败");
        }
        List<ProductItemDO> removeProductItems = new ArrayList<>();
        LambdaQueryWrapper<ProductItemDO> queryWrapper = Wrappers.lambdaQuery(ProductItemDO.class)
                .eq(ProductItemDO::getProductId, productDO.getId());
        List<ProductItemDO> productItemDOS = productItemService.list(queryWrapper);
        for (ProductItemDO productItemDO : productItemDOS) {
            List<ProductItemDO> collect = productUpdateDTO.getProductItems().stream()
                    .filter(p -> productItemDO.getSkuCode().equals(p.getSkuCode())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)) {
                removeProductItems.add(productItemDO);
            }
        }
        if (CollectionUtils.isNotEmpty(removeProductItems)) {
            // 删除在购物车中的sku
            List<String> skus = removeProductItems.stream().map(ProductItemDO::getSkuCode).collect(Collectors.toList());
            Result result = orderFeignClient.removeCartltemBySkus(skus);
            if (!result.getStatus()) {
                throw new BaseException("删除购物车中的商品失败!! : " + result.getMessage());
            }
            LambdaQueryWrapper<ProductItemDO> deleteQw = Wrappers.lambdaQuery(ProductItemDO.class)
                    .in(ProductItemDO::getSkuCode, skus);
            productItemService.remove(deleteQw);
        }
        List<ProductItemDO> collect = productUpdateDTO.getProductItems()
                .stream().map(productItemDO -> productItemDO.setProductId(productDO.getId())).collect(Collectors.toList());
        boolean b = productItemService.saveOrUpdateBatch(collect);
        if (!b) {
            throw new BaseException("添加商品规格参数失败");
        }
        UrlManageDTO urlManageDTO = new UrlManageDTO();
        urlManageDTO.setName(productUpdateDTO.getName());
        urlManageDTO.setUrl(URL_NAME + productUpdateDTO.getId());
        Result result = userFeignClient.updateUrl(urlManageDTO);
        if (!result.getStatus()) {
            throw new BaseException("修改商品url失败！！");
        }
        return Result.success();
    }


    @Override
    public Result setRecommen(ProductUpdateDTO productUpdateDTO) {
        LambdaUpdateWrapper<ProductDO> uw = Wrappers.lambdaUpdate(ProductDO.class)
                .set(ProductDO::getRecommenIds, productUpdateDTO.getRecommenIds())
                .eq(ProductDO::getId, productUpdateDTO.getId());
        return this.update(uw) ? Result.success() : Result.fail();
    }

    @Override
    public Result bindingCoupon(Long id, Long couponId) {
        return couponFeignClient.bindingCoupon(id, couponId);
    }

    @Override
    public Result deleteCoupon(Long id, Long couponId) {
        return couponFeignClient.deleteCoupon(id, couponId);
    }

    @Override
    public Result pageList(ProductPageDTO productPageDTO) {
        Page<ProductPageVO> page = new Page<>(productPageDTO.getCurrPage(), productPageDTO.getPageSize());
        return Result.success(this.baseMapper.pageList(page, productPageDTO));
    }

    @Override
    public Result findRecommenInfo(Long id) {
        ProductDO productDO = getById(id);
        List<String> result = Arrays.asList(productDO.getRecommenIds().split(","));
        List<Long> ids = result.stream().map(Long::parseLong).collect(Collectors.toList());
        List<ProductDO> products = this.listByIds(ids);
        List<Long> categoryIds = products.stream().map(ProductDO::getProductCategoryId).collect(Collectors.toList());
        List<ProductCategoryDO> productCategoryDOS = productCategoryService.listByIds(categoryIds);
        Map<Long, ProductCategoryDO> categoryMap = productCategoryDOS.parallelStream().collect(Collectors.toMap(ProductCategoryDO::getId, p -> p));
        List<ProductRecommenInfoVO> collect = products.stream().map(p -> {
            ProductRecommenInfoVO productRecommenInfoVO = new ProductRecommenInfoVO();
            productRecommenInfoVO.setId(p.getId());
            productRecommenInfoVO.setName(p.getName());
            productRecommenInfoVO.setCategoryId(p.getProductCategoryId());
            productRecommenInfoVO.setCategoryName(categoryMap.get(p.getProductCategoryId()).getName());
            return productRecommenInfoVO;
        }).collect(Collectors.toList());
        return Result.success(collect);
    }


    @Override
    public Result findById(Long id) {
        ProductDO productDO = getById(id);
        LambdaQueryWrapper<ProductItemDO> queryWrapper = Wrappers.lambdaQuery(ProductItemDO.class)
                .eq(ProductItemDO::getProductId, productDO.getId());
        List<ProductItemDO> list = productItemService.list(queryWrapper);
        ProductVO productVO = BeanUtils.copyProperties(productDO, ProductVO.class);
        productVO.setProductItems(list);
        return Result.success(productVO);
    }

    @Override
    public Result findByCategoryId(Long categoryId) {
        LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class)
                .eq(ProductDO::getProductCategoryId, categoryId);
        List<ProductDO> list = this.list(queryWrapper);
        List<Map<String, Object>> collect = list.stream().map(p -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", p.getId());
            map.put("name", p.getName());
            return map;
        }).collect(Collectors.toList());
        return Result.success(collect);
    }

    @Override
    public Result findCouponByProductId(Long id) {
        Result<List<CouponVO>> result = couponFeignClient.findCouponByProductIds(Collections.singletonList(id));
        if (!result.getStatus()) {
            return Result.fail();
        }
        List<CouponVO> entry = result.getEntry();
        if (CollectionUtils.isNotEmpty(entry)) {
            List<CouponVO> collect = entry.stream().filter(couponVO -> couponVO.getType() == 5).collect(Collectors.toList());
            return Result.success(collect);
        }
        return Result.success(entry);
    }

    @Transactional
    @Override
    public Result delete(Long id) {
        this.removeById(id);
        LambdaQueryWrapper<ProductItemDO> queryWrapperProductItemDO = Wrappers.lambdaQuery(ProductItemDO.class)
                .eq(ProductItemDO::getProductId, id);
        productItemService.remove(queryWrapperProductItemDO);
        LambdaQueryWrapper<ProductGiveDO> queryWrapper = Wrappers.lambdaQuery(ProductGiveDO.class)
                .eq(ProductGiveDO::getProductId, id);
        productGiveService.remove(queryWrapper);
        // 删除购物车中的商品
        orderFeignClient.removeCartltemByProductId(id);
        // 删除连接地址
        userFeignClient.deleteUrl(id);
        return Result.success();
    }

    @Override
    public Result findGiveAll() {
        List<ProductVO> products = this.baseMapper.findGiveAll();
        return Result.success(products);
    }

    @Override
    public Result give(List<ProductGiveDO> productGives) {
        return productGiveService.saveOrUpdateBatch(productGives) ? Result.success() : Result.fail();
    }

    @Override
    public Result findGive(Long id) {
        LambdaQueryWrapper<ProductGiveDO> queryWrapper = Wrappers.lambdaQuery(ProductGiveDO.class)
                .eq(ProductGiveDO::getProductId, id).eq(ProductGiveDO::getType, 1);
        return Result.success(productGiveService.list(queryWrapper));
    }
    /**
     * 维护满减信息
     *
     * @param productDetailedVO
     * @param productFullReductionDOS
     */
    //private void maintenanceFullReduction(ProductDetailedVO productDetailedVO, List<ProductFullReductionDO> productFullReductionDOS) {
    //    Optional<ProductItemDO> min = productDetailedVO.getProductItems().stream().min(Comparator.comparing(ProductItemDO::getPrice));
    //    if (min.isPresent()) {
    //        BigDecimal price = min.get().getPrice();
    //        // 599 - 699  600
    //        productFullReductionDOS = productFullReductionDOS.stream().sorted(Comparator.comparing(ProductFullReductionDO::getFullPrice).reversed()).collect(Collectors.toList());
    //        for (ProductFullReductionDO productFullReductionDO : productFullReductionDOS) {
    //            if (price.compareTo(productFullReductionDO.getFullPrice()) != -1) {
    //                productDetailedVO.setProductFullReduction(productFullReductionDO);
    //                break;
    //            }
    //        }
    //    }
    //}
//

    /**
     * 维护用户优惠卷
     *
     * @param future3
     * @param productDetailedVO
     * @param coupons
     * @throws InterruptedException
     * @throws java.util.concurrent.ExecutionException
     */
    private void maintenanceMyCoupon(CompletableFuture<List<CouponVO>> future3, ProductDetailedVO productDetailedVO, List<CouponVO> coupons) throws InterruptedException, java.util.concurrent.ExecutionException {
        List<CouponVO> userCoupons = future3.get();
        for (CouponVO coupon : coupons) {
            if (!userCoupons.contains(coupon)) {
                coupon.setIsReceive(false);
            }
        }
    }
}
